package nx.simple3d.data;

import java.awt.Graphics;

/**
 * Creates a camera, which by default is placed at 0,0,0 and
 * looks in the direcion of the x-axis.
 * @author simon
 *
 */
public class Camera{
  private Matrix transformation;
  private Matrix projection; 
  private VectorBase vb;
  private TriangleBase tb;
  private World world;
  
  /**
   * Constructs the camera.
   * @param vb The vector base holding the vectors to process
   * @param world The world with meshes
   */
  public Camera(World world) {
    this.world = world;
    vb = VectorBase.getInstance();
    tb = TriangleBase.getInstance();
    transformation = new Matrix();
  }
  
  /**
   * Use this method to moe and rotate the camera.
   * @return the matrix, which transforms the position and
   * orientation of the camera.
   */
  public Matrix getTransformation(){
    return transformation;
  }
  
  public Matrix getProjection(){
    if(projection == null){
      projection = transformation.getInverted();
    }
    else{
      transformation.updateInverted(projection);
    }
    return projection;
  }
  
  /**
   * This method is supposed to be called by the canvas which
   * shows the scene from the cameras point of view.
   * @param g The graphics-canvas to paint on
   */
  public void expose(Graphics g, double width, double height){
    int screenCoordsIndex, triIndex;
    int x1, y1, x2, y2, x3, y3;
    double vby1, vby2, vby3, vbz1, vbz2, vbz3;
    int vertexIndex;
    
    for(Mesh mesh : world.getMeshes()){
      screenCoordsIndex = mesh.getOffsetToVectorSection(Mesh.VECTOR_SECTIONS.READABLE);
      for(triIndex = mesh.getTriangleOffset(); triIndex <  mesh.getTriangleOffset() + mesh.getTriangleCount(); triIndex++){
        vertexIndex = screenCoordsIndex + tb.a[triIndex];
        vbz1 = vb.z[vertexIndex];
        vby1 = vb.y[vertexIndex];
        vertexIndex = screenCoordsIndex + tb.b[triIndex];
        vbz2 = vb.z[vertexIndex];
        vby2 = vb.y[vertexIndex];
        vertexIndex = screenCoordsIndex + tb.c[triIndex];
        vbz3 = vb.z[vertexIndex];
        vby3 = vb.y[vertexIndex];
        
        if(!Double.isNaN(vbz1) && !Double.isNaN(vbz2) && !Double.isNaN(vbz3)){   
          x1 = (int)(width / 2 + vbz1 * width);
          y1 = (int)(height/ 2 - vby1 * width);
          x2 = (int)(width / 2 + vbz2 * width);
          y2 = (int)(height/ 2 - vby2 * width);
          x3 = (int)(width / 2 + vbz3 * width);
          y3 = (int)(height/ 2 - vby3 * width);
          
          g.drawLine(x1, y1, x2, y2);
          g.drawLine(x2, y2, x3, y3);
          g.drawLine(x3, y3, x1, y1);
        }
      }
    }
  }
  
  public World getWorld(){
    return world;
  }
  
  /**
   * Used to project vertices on a y/z plane seen in from the origin in the direction of the x-axis
   * @param mesh The mesh to project
   * @return the index in the vectorbase from where the y and z coordinates are the calculated screencoordinates
   */
  public void project(Mesh mesh){
    int original = mesh.getOffsetToVectorSection(Mesh.VECTOR_SECTIONS.ORIGINAL);
    int target = mesh.getOffsetToVectorSection(Mesh.VECTOR_SECTIONS.WRITEABLE);
    int vertexCount = mesh.getVerticesCount();
    
    mesh.getProjectedTransformation(getProjection()).apply(original, target, vertexCount);
    
    vb.scaleXToValue(target, vertexCount, 1);
    mesh.writingCompleted();
  }
}
